#include "FPCH.h"

#include "FText.h"

#include "FGlyphMap.h"
#include "FFactory.h"
#include "FLayoutInterface.h"
#include "FTextInstructionsUtf16.h"
#include "FGlyphTransformWavy.h"

namespace Font
{

	Text::Text(Library* a_Library, FaceHandle* a_Face)
	{
		m_Library = a_Library;

		m_Font = a_Face;

		m_MessagePosStart.Clear();
		m_MessagePosEnd.Clear();
		m_MessageDimensions.Clear();

		m_Instructions = NULL;

		m_Cursor.Clear();
		m_Transform.Identity();

		m_GlyphTransform = new GlyphTransformWavy(a_Library);
	}
	
	Text::~Text()
	{
		
	}

	void Text::SetInstructions(TextInstructions* a_Instructions)
	{
		m_Instructions = a_Instructions;

		if (m_Instructions->GetTextLength() > 0)
		{
			WriteInstructions();
		}
	}

	void Text::SetCursor(const tb::Vec2& a_Cursor)
	{
		m_Cursor = a_Cursor;
	}

	void Text::SetTransform(const tb::Mat3x3& a_Transform)
	{
		m_Transform = a_Transform;
	}

	bool Text::Write(const unsigned char* a_Message, unsigned int a_Length)
	{
		if (!m_Instructions)
		{
			m_Instructions = new TextInstructionsUtf16(m_Library);
		}

		if (!m_Instructions->SetTextData(a_Message))
		{
			LOG_ERROR("Could not parse message.");
			return false;
		}

		return WriteInstructions();
	}

	bool Text::WriteInstructions()
	{
		for (size_t i = 0; i < m_Display.size(); i++)
		{
			if (m_Display[i])
			{
				m_Display[i]->Reserve(m_Instructions->GetTextLength());
				m_Display[i]->Clear();
			}
		}

		tb::Vec2 target_pos = m_Cursor;
		GlyphEntry* prev = NULL;

		m_Display.clear();
		TextDisplay* display_bind = NULL;

		bool char_first = false;
		m_MessagePosStart.Clear();
		m_MessagePosEnd.Clear();
		m_MessageDimensions.Clear();

		wchar_t c_prev = 0;
		int page_prev = -1;
		while (m_Instructions->AdvanceCommand())
		{
			const TextCommand& command_current = m_Instructions->GetCommand();

			if (command_current.type == Instruction::eNewLine)
			{
				target_pos.x = 0.f;
				target_pos.y += (float)m_Font->GetHeightNewLine();
				continue;
			}
			else if (command_current.type == Instruction::eTab)
			{ 
				continue;
			}

			// character instruction

			wchar_t c = (wchar_t)command_current.data;
			GlyphEntry* curr = m_Font->GetGlyphMap()->GetGlyphEntry(c);
			if (!curr) { continue; }

			if (curr->bitmap)
			{
				if (curr->page_id != page_prev)
				{
					bool load = false;

					if ((size_t)curr->page_id < m_Display.size())
					{
						display_bind = m_Display[curr->page_id];
						if (!display_bind) { load = true; }
					}
					else
					{
						m_Display.resize(curr->page_id + 1);
						load = true;
					}

					if (load)
					{
						display_bind = m_Library->GetFactory()->CreateTextDisplay(m_Instructions->GetTextLength());
						m_Display[curr->page_id] = display_bind;
					}

					display_bind->SetPage(m_Font->GetGlyphMap()->GetPage(curr->page_id));
				}
				page_prev = curr->page_id;

				target_pos.x += (float)m_Font->GetKerning(c, c_prev);

				m_GlyphTransform->Transform(target_pos, curr);
				tb::Vec2 transformed_ul = m_GlyphTransform->GetCornerUpperLeft();
				tb::Vec2 transformed_ur = m_GlyphTransform->GetCornerUpperRight();
				tb::Vec2 transformed_ll = m_GlyphTransform->GetCornerLowerLeft();
				tb::Vec2 transformed_lr = m_GlyphTransform->GetCornerLowerRight();

				// get message dimensions

				if (!char_first) 
				{ 
					m_MessagePosStart = transformed_ul; 
					char_first = true;
				}
				m_MessagePosEnd = transformed_lr;

				// put in display buffers

				display_bind->AddPosition(transformed_ul.x, transformed_ul.y); 
				display_bind->AddTexCoord(curr->tex_start.x, curr->tex_start.y);

				display_bind->AddPosition(transformed_ll.x, transformed_ll.y); 
				display_bind->AddTexCoord(curr->tex_start.x, curr->tex_end.y);

				display_bind->AddPosition(transformed_ur.x, transformed_ur.y); 
				display_bind->AddTexCoord(curr->tex_end.x, curr->tex_start.y);

				display_bind->AddPosition(transformed_lr.x, transformed_lr.y); 
				display_bind->AddTexCoord(curr->tex_end.x, curr->tex_end.y);

				display_bind->NextQuad();

				c_prev = c;
			}

			target_pos.x += curr->advance;
		}

		m_MessageDimensions.x = m_MessagePosEnd.x - m_MessagePosStart.x;
		m_MessageDimensions.y = m_MessagePosEnd.y - m_MessagePosStart.y;

		return true;
	}

	void Text::Render()
	{
		for (size_t i = 0; i < m_Display.size(); i++)
		{
			if (m_Display[i]) { m_Display[i]->Render(); }
		}
	}

	void Text::Layout()
	{

	}

	const tb::Vec2& Text::GetDimensions()
	{
		return m_MessageDimensions;
	}

	const tb::Vec2& Text::GetPositionStart()
	{
		return m_MessagePosStart;
	}

	const tb::Vec2& Text::GetPositionEnd()
	{
		return m_MessagePosEnd;
	}

	void Text::SetFont(FaceHandle* a_Font)
	{
		for (std::vector<TextDisplay*>::iterator it = m_Display.begin(); it != m_Display.end();)
		{
			delete *it;
			it = m_Display.erase(it);
		}

		m_Font = a_Font;

		if (m_Instructions) { WriteInstructions(); }
	}



}; // namespace Font